4 * Gestion de l'affichage des saisies
6 * @package SPIP\Saisies\Saisies
10 if (!defined('_ECRIRE_INC_VERSION')) {
14 // Différentes méthodes pour trouver les saisies
15 include_spip('inc/saisies_lister');
17 // Différentes méthodes pour manipuler une liste de saisies
18 include_spip('inc/saisies_manipuler');
20 // Les outils pour afficher les saisies et leur vue
21 include_spip('inc/saisies_afficher');
24 * Cherche la description des saisies d'un formulaire CVT dont on donne le nom
26 * @param string $form Nom du formulaire dont on cherche les saisies
27 * @param array $args Tableau d'arguments du formulaire
28 * @return array Retourne les saisies du formulaire sinon false
30 function saisies_chercher_formulaire($form, $args) {
32 $fonction_saisies = charger_fonction('saisies', 'formulaires/'.$form, true)
33 and $saisies = call_user_func_array($fonction_saisies, $args)
34 and is_array($saisies)
35 // On passe les saisies dans un pipeline normé comme pour CVT
36 and $saisies = pipeline(
39 'args' => array('form' => $form, 'args' => $args),
43 // Si c'est toujours un tableau après le pipeline
44 and is_array($saisies)
53 * Cherche une saisie par son id, son nom ou son chemin et renvoie soit la saisie, soit son chemin
55 * @param array $saisies Un tableau décrivant les saisies
56 * @param unknown_type $id_ou_nom_ou_chemin L'identifiant ou le nom de la saisie à chercher ou le chemin sous forme d'une liste de clés
57 * @param bool $retourner_chemin Indique si on retourne non pas la saisie mais son chemin
58 * @return array Retourne soit la saisie, soit son chemin, soit null
60 function saisies_chercher($saisies, $id_ou_nom_ou_chemin, $retourner_chemin = false) {
61 if (is_array($saisies) and $id_ou_nom_ou_chemin) {
62 if (is_string($id_ou_nom_ou_chemin)) {
63 $nom = $id_ou_nom_ou_chemin;
64 // identifiant ? premier caractere @
65 $id = ($nom[0] == '@');
67 foreach ($saisies as $cle => $saisie) {
68 $chemin = array($cle);
69 // notre saisie est la bonne ?
70 if ($nom == ($id ?
$saisie['identifiant'] : $saisie['options']['nom'])) {
71 return $retourner_chemin ?
$chemin : $saisie;
72 // sinon a telle des enfants ? et si c'est le cas, cherchons dedans
73 } elseif (isset($saisie['saisies']) and is_array($saisie['saisies']) and $saisie['saisies']
74 and ($retour = saisies_chercher($saisie['saisies'], $nom, $retourner_chemin))) {
75 return $retourner_chemin ?
array_merge($chemin, array('saisies'), $retour) : $retour;
79 elseif (is_array($id_ou_nom_ou_chemin)) {
80 $chemin = $id_ou_nom_ou_chemin;
83 // On vérifie l'existence quand même
84 foreach ($chemin as $cle) {
85 if (isset($saisie[$cle])) {
86 $saisie = $saisie[$cle];
92 // Si c'est une vraie saisie
93 if ($saisie['saisie'] and $saisie['options']['nom']) {
94 return $retourner_chemin ?
$chemin : $saisie;
103 * Génère un nom unique pour un champ d'un formulaire donné
105 * @param array $formulaire
106 * Le formulaire à analyser
107 * @param string $type_saisie
108 * Le type de champ dont on veut un identifiant
110 * Un nom unique par rapport aux autres champs du formulaire
112 function saisies_generer_nom($formulaire, $type_saisie) {
113 $champs = saisies_lister_champs($formulaire);
115 // Tant que type_numero existe, on incrémente le compteur
117 while (array_search($type_saisie.'_'.$compteur, $champs) !== false) {
121 // On a alors un compteur unique pour ce formulaire
122 return $type_saisie.'_'.$compteur;
126 * Crée un identifiant Unique
127 * pour toutes les saisies donnees qui n'en ont pas
129 * @param Array $saisies Tableau de saisies
130 * @param Bool $regenerer Régénère un nouvel identifiant pour toutes les saisies ?
131 * @return Array Tableau de saisies complété des identifiants
133 function saisies_identifier($saisies, $regenerer = false) {
134 if (!is_array($saisies)) {
138 foreach ($saisies as $k => $saisie) {
139 $saisies[$k] = saisie_identifier($saisie, $regenerer);
146 * Crée un identifiant Unique
147 * pour la saisie donnee si elle n'en a pas
148 * (et pour ses sous saisies éventuels)
150 * @param Array $saisie Tableau d'une saisie
151 * @param Bool $regenerer Régénère un nouvel identifiant pour la saisie ?
152 * @return Array Tableau de la saisie complété de l'identifiant
154 function saisie_identifier($saisie, $regenerer = false) {
155 if (!isset($saisie['identifiant']) or !$saisie['identifiant']) {
156 $saisie['identifiant'] = uniqid('@');
157 } elseif ($regenerer) {
158 $saisie['identifiant'] = uniqid('@');
160 if (isset($saisie['saisies']) and is_array($saisie['saisies'])) {
161 $saisie['saisies'] = saisies_identifier($saisie['saisies'], $regenerer);
168 * Vérifier tout un formulaire tel que décrit avec les Saisies
170 * @param array $formulaire Le contenu d'un formulaire décrit dans un tableau de Saisies
171 * @param bool $saisies_masquees_nulles Si TRUE, les saisies masquées selon afficher_si ne seront pas verifiées, leur valeur étant forcée a NULL. Cette valeur NULL est transmise à traiter (via set_request).
172 * @param array &$erreurs_fichiers pour les saisies de type fichiers, un tableau qui va stocker champs par champs, puis fichier par fichier, les erreurs de chaque fichier, pour pouvoir ensuite éventuellement supprimer les fichiers erronées de $_FILES
173 * @return array Retourne un tableau d'erreurs
175 function saisies_verifier($formulaire, $saisies_masquees_nulles = true, &$erreurs_fichiers = array()) {
176 include_spip('inc/verifier');
178 $verif_fonction = charger_fonction('verifier', 'inc', true);
180 if ($saisies_masquees_nulles) {
181 $formulaire = saisies_verifier_afficher_si($formulaire);
184 $saisies = saisies_lister_par_nom($formulaire);
185 foreach ($saisies as $saisie) {
186 $obligatoire = isset($saisie['options']['obligatoire']) ?
$saisie['options']['obligatoire'] : '';
187 $champ = $saisie['options']['nom'];
188 $file = (($saisie['saisie'] == 'input' and isset($saisie['options']['type']) and $saisie['options']['type'] == 'file') or $saisie['saisie'] == 'fichiers');
189 $verifier = isset($saisie['verifier']) ?
$saisie['verifier'] : false;
191 // Cas de la saisie 'fichiers':
192 if ($saisie['saisie'] == 'fichiers') {
193 $infos_fichiers_precedents = _request('cvtupload_fichiers_precedents');
194 if (isset($infos_fichiers_precedents[$champ])) { // si on a déjà envoyé des infos avants
195 $valeur = $_FILES[$champ]; // on ne met pas true, car il faudra aussi vérifier les nouveaux fichiers du même champ qui viennent d'être envoyés.
196 } elseif (isset($_FILES[$champ]['error'])) {//si jamais on a déja envoyé quelque chose dans le précédent envoi = ok
197 $valeur = null; //On considère que par défaut on a envoyé aucun fichiers
198 foreach ($_FILES[$champ]['error'] as $err) {
200 //Si un seul fichier a été envoyé, même avec une erreur,
201 // on considère que le critère obligatoire est rempli.
202 // Il faudrait que verifier/fichiers.php vérifier les autres types d'erreurs.
203 // Voir http://php.net/manual/fr/features.file-upload.errors.php
204 $valeur = $_FILES[$champ];
208 } elseif (!isset($_FILES[$champ])) {
212 // Tout type de saisie, sauf fichiers
214 // Si le nom du champ est un tableau indexé, il faut parser !
215 if (preg_match('/([\w]+)((\[[\w]+\])+)/', $champ, $separe)) {
216 $valeur = _request($separe[1]);
217 preg_match_all('/\[([\w]+)\]/', $separe[2], $index);
218 // On va chercher au fond du tableau
219 foreach ($index[1] as $cle) {
220 $valeur = isset($valeur[$cle]) ?
$valeur[$cle] : null;
223 // Sinon la valeur est juste celle du nom
224 $valeur = _request($champ);
228 // Pour la saisie "destinataires" il faut filtrer si jamais on a mis un premier choix vide
229 if ($saisie['saisie'] == 'destinataires') {
230 $valeur = array_filter($valeur);
233 // On regarde d'abord si le champ est obligatoire
236 and $obligatoire != 'non'
238 ($file and $valeur==null)
241 or (is_string($valeur) and trim($valeur) == '')
242 or (is_array($valeur) and count($valeur) == 0)
247 (isset($saisie['options']['erreur_obligatoire']) and $saisie['options']['erreur_obligatoire'])
248 ?
$saisie['options']['erreur_obligatoire']
249 : _T('info_obligatoire');
252 // On continue seulement si ya pas d'erreur d'obligation et qu'il y a une demande de verif
253 if ((!isset($erreurs[$champ]) or !$erreurs[$champ]) and is_array($verifier) and $verif_fonction) {
254 // Si on fait une vérification de type fichiers, il n'y a pas vraiment de normalisation, mais un retour d'erreur fichiers par fichiers
255 if ($verifier['type'] == 'fichiers') {
256 $normaliser = array();
261 // Si le champ n'est pas valide par rapport au test demandé, on ajoute l'erreur
262 $options = isset($verifier['options']) ?
$verifier['options'] : array();
263 if ($erreur_eventuelle = $verif_fonction($valeur, $verifier['type'], $options, $normaliser)) {
264 $erreurs[$champ] = $erreur_eventuelle;
266 if ($verifier['type'] == 'fichiers') { // Pour les vérification/saisies de type fichiers, ajouter les erreurs détaillées par fichiers dans le tableau des erreurs détaillées par fichier
267 $erreurs_fichiers[$champ] = $normaliser;
271 // S'il n'y a pas d'erreur et que la variable de normalisation a été remplie, on l'injecte dans le POST
272 elseif (!is_null($normaliser) and $verifier['type'] != 'fichiers') {
273 set_request($champ, $normaliser);
278 // Last but not least, on passe nos résultats à un pipeline
283 'formulaire' => $formulaire,
284 'saisies' => $saisies
294 * Applatie une description tabulaire
295 * @param string $tab Le tableau à aplatir
296 * @return $nouveau_tab
298 function saisies_aplatir_tableau($tab) {
299 $nouveau_tab = array();
301 foreach ($tab as $entree => $contenu) {
302 if (is_array($contenu)) {
303 foreach ($contenu as $cle => $valeur) {
304 $nouveau_tab[$cle] = $valeur;
307 $nouveau_tab[$entree] = $contenu;
315 * Applatie une description chaînée, en supprimant les sous-groupes.
316 * @param string $chaine La chaîne à aplatir
319 function saisies_aplatir_chaine($chaine) {
320 return trim(preg_replace("#(?:^|\n)(\*(?:.*)|/\*)\n#i", "\n", $chaine));
324 * Transforme une chaine en tableau avec comme principe :
326 * - une ligne devient une case
327 * - si la ligne est de la forme truc|bidule alors truc est la clé et bidule la valeur
328 * - si la ligne commence par * alors on commence un sous-tableau
329 * - si la ligne est égale à /*, alors on fini le sous-tableau
331 * @param string $chaine Une chaine à transformer
332 * @param string $separateur Séparateur utilisé
333 * @return array Retourne un tableau PHP
335 function saisies_chaine2tableau($chaine, $separateur = "\n") {
336 if ($chaine and is_string($chaine)) {
340 // On découpe d'abord en lignes
341 $lignes = explode($separateur, $chaine);
342 foreach ($lignes as $i => $ligne) {
343 $ligne = trim(trim($ligne), '|');
344 // Si ce n'est pas une ligne sans rien
346 // si ca commence par * c'est qu'on va faire un sous tableau
347 if (strpos($ligne, '*') === 0) {
349 $soustab_cle = _T_ou_typo(substr($ligne, 1), 'multi');
350 if (!isset($tableau[$soustab_cle])) {
351 $tableau[$soustab_cle] = array();
353 } elseif ($ligne == '/*') {//si on finit sous tableau
356 //sinon c'est une entrée normale
357 // Si on trouve un découpage dans la ligne on fait cle|valeur
358 if (strpos($ligne, '|') !== false) {
359 list($cle,$valeur) = explode('|', $ligne, 2);
360 // permettre les traductions de valeurs au passage
361 if ($soustab == true) {
362 $tableau[$soustab_cle][$cle] = _T_ou_typo($valeur, 'multi');
364 $tableau[$cle] = _T_ou_typo($valeur, 'multi');
367 // Sinon on génère la clé
368 if ($soustab == true) {
369 $tableau[$soustab_cle][$i] = _T_ou_typo($ligne, 'multi');
371 $tableau[$i] = _T_ou_typo($ligne, 'multi');
379 elseif (is_array($chaine)) {
380 // Si c'est déjà un tableau on lui applique _T_ou_typo (qui fonctionne de manière récursive avant de le renvoyer
381 return _T_ou_typo($chaine, 'multi');
389 * Transforme un tableau en chaine de caractères avec comme principe :
391 * - une case de vient une ligne de la chaine
392 * - chaque ligne est générée avec la forme cle|valeur
393 * - si une entrée du tableau est elle même un tableau, on met une ligne de la forme *clef
394 * - pour marquer que l'on quitte un sous-tableau, on met une ligne commencant par /*, sauf si on bascule dans un autre sous-tableau.
396 * @param array $tableau Tableau à transformer
397 * @return string Texte représentant les données du tableau
399 function saisies_tableau2chaine($tableau) {
400 if ($tableau and is_array($tableau)) {
402 $avant_est_tableau = false;
404 foreach ($tableau as $cle => $valeur) {
405 if (is_array($valeur)) {
406 $avant_est_tableau = true;
407 $ligne=trim("*$cle");
408 $chaine .= "$ligne\n";
409 $chaine .= saisies_tableau2chaine($valeur)."\n";
411 if ($avant_est_tableau == true) {
412 $avant_est_tableau = false;
415 $ligne = trim("$cle|$valeur");
416 $chaine .= "$ligne\n";
419 $chaine = trim($chaine);
423 elseif (is_string($tableau)) {
424 // Si c'est déjà une chaine on la renvoie telle quelle
433 * Transforme une valeur en tableau d'élements si ce n'est pas déjà le cas
435 * @param mixed $valeur
436 * @return array Tableau de valeurs
438 function saisies_valeur2tableau($valeur) {
439 if (is_array($valeur)) {
443 if (!strlen($valeur)) {
447 $t = saisies_chaine2tableau($valeur);
452 // qu'une seule valeur, c'est qu'elle a peut etre un separateur a virgule
453 // et a donc une cle est 0 dans ce cas la d'ailleurs
455 $t = saisies_chaine2tableau($t[0], ',');
462 * Pour les saisies multiples (type checkbox) proposant un choix alternatif,
463 * retrouve à partir des data de choix proposés
464 * et des valeurs des choix enregistrés
465 * le texte enregistré pour le choix alternatif.
468 * @param array $valeur
469 * @return string choix_alternatif
471 function saisies_trouver_choix_alternatif($data, $valeur) {
472 if (!is_array($valeur)) {
473 $valeur = saisies_chaine2tableau($valeur) ;
475 if (!is_array($data)) {
476 $data = saisies_chaine2tableau($data) ;
479 $choix_theorique = array_keys($data);
480 $choix_alternatif = array_values(array_diff($valeur, $choix_theorique));
481 if (isset($choix_alternatif[0])) {
482 return $choix_alternatif[0]; //on suppose que personne ne s'est amusé à proposer deux choix alternatifs
489 * Génère une page d'aide listant toutes les saisies et leurs options
491 * Retourne le résultat du squelette `inclure/saisies_aide` auquel
492 * on a transmis toutes les saisies connues.
494 * @return string Code HTML
496 function saisies_generer_aide() {
497 // On a déjà la liste par saisie
498 $saisies = saisies_lister_disponibles();
500 // On construit une liste par options
502 foreach ($saisies as $type_saisie => $saisie) {
503 $options_saisie = saisies_lister_par_nom($saisie['options'], false);
504 foreach ($options_saisie as $nom => $option) {
505 // Si l'option n'existe pas encore
506 if (!isset($options[$nom])) {
507 $options[$nom] = _T_ou_typo($option['options']);
509 // On ajoute toujours par qui c'est utilisé
510 $options[$nom]['utilisee_par'][] = $type_saisie;
512 ksort($options_saisie);
513 $saisies[$type_saisie]['options'] = $options_saisie;
517 return recuperer_fond(
518 'inclure/saisies_aide',
520 'saisies' => $saisies,
521 'options' => $options
527 * Le tableau de saisies a-t-il une option afficher_si ?
529 * @param array $saisies Un tableau de saisies
532 function saisies_afficher_si($saisies) {
533 $saisies = saisies_lister_par_nom($saisies, true);
535 // Dès qu'il y a au moins une option afficher_si, on l'active
536 foreach ($saisies as $saisie) {
537 if (isset($saisie['options']['afficher_si'])) {
547 * Le tableau de saisies a-t-il une option afficher_si_remplissage ?
549 * @param array $saisies Un tableau de saisies
552 function saisies_afficher_si_remplissage($saisies) {
553 $saisies = saisies_lister_par_nom($saisies, true);
555 // Dès qu'il y a au moins une option afficher_si_remplissage, on l'active
556 foreach ($saisies as $saisie) {
557 if (isset($saisie['options']['afficher_si_remplissage'])) {